home *** CD-ROM | disk | FTP | other *** search
/ Pascal Super Library / Pascal Super Library (CW International)(1997).bin / TSR / TSRSRC35 / WATCH.ASM < prev    next >
Assembly Source File  |  1993-10-18  |  27KB  |  743 lines

  1. ;WATCH.ASM
  2. ;resident routine watches programs going resident
  3. ;and keeps a list of interrupt vector changes in an internal data structure
  4. ;==============================================================================
  5. ; to be assembled by TASM
  6. ; Copyright (c) 1986,1993 Kim Kokkonen, TurboPower Software.
  7. ; May be freely distributed but not sold except by permission.
  8. ; telephone: 719-260-6641, Compuserve 76004,2611
  9. ;==============================================================================
  10. ; version 2.2  3/4/87
  11. ;   First release, version to be consistent with MAPMEM.PAS
  12. ; :
  13. ; long intervening history
  14. ; :
  15. ; version 3.0  9/24/91
  16. ;   add tracking for TSRs that unload themselves
  17. ;   add support for TSRs loaded high
  18. ;   WATCH may be loaded high
  19. ; version 3.1  11/4/91
  20. ;   rewrite again to solve problems with SWAPMM, FSP, DATAPATH, DATAMON
  21. ; version 3.2  11/22/91
  22. ;   change method of accessing high memory
  23. ;   deal with DOS 5 MODE int trapping (int seg < psp seg)
  24. ; version 3.3  1/8/92
  25. ;   relocate AddChain code so that it doesn't get overwritten if there
  26. ;     are lots of initial memory blocks
  27. ; version 3.4  2/14/92
  28. ;   no change
  29. ; version 3.5 10/18/93
  30. ;   new hybrid method for finding high memory
  31. ;==============================================================================
  32. ;
  33. ;uncomment following line to generate more publics in MAP file
  34. ;       debug   = 1
  35.  
  36. cseg    segment public para
  37.         assume  cs:cseg, ds:nothing, es:nothing, ss:nothing
  38.         locals  @@
  39.  
  40.         org     080H
  41. cmdline label   byte                    ;pointer to command line
  42.  
  43.         org     100H
  44. pentry: jmp     init
  45.  
  46. ;always put the following in WATCH.MAP to update MEMU.PAS
  47. public nextchange,emesg,changevectors,origvectors
  48.  
  49. ;***********************************************************************
  50. ;data structures part of COM file
  51.                 even
  52. nextchange      dw      0               ;next position to write in changes area
  53.  
  54. firstmcb        dw      ?               ;first MCB segment
  55. firsthimcb      dw      0               ;first MCB segment in high memory
  56.  
  57. ;temporary stack used by interrupt handler
  58. newsp           dw      ?               ;initial stack pointer
  59. newss           dw      ?               ;segment of our temporary stack (=cseg)
  60. tmpret          dw      ?               ;used while switching stacks
  61.  
  62. ;information saved about the calling program
  63. oldsp           dw      ?               ;stack pointer
  64. oldss           dw      ?               ;stack segment
  65.  
  66. ;previous interrupt handlers
  67. dos_int         label dword
  68. old21           dw 2 dup (?)            ;old int21 vector
  69. tsr_int         label dword
  70. old27           dw 2 dup (?)            ;old int27 vector
  71.  
  72. ;XMS access
  73. xmsadr  label   dword                   ;XMS control address
  74. xmsxxx  dw      2 dup (0)
  75.  
  76. ;id code for a PSP data block
  77. pspid           equ     0FFFFH          ;id used to indicate a PSP block
  78.  
  79. ;structure of a changevectors data block
  80. pspblock        struc
  81.                 id      dw      ?       ;id word, always pspid
  82.                 psp     dw      ?       ;psp segment
  83.                 len     dw      ?       ;length of psp
  84.                 unu1    dw      ?       ;unused
  85. pspblock        ends
  86. vecblock        struc
  87.                 vec     dw      ?       ;vector number 0..255
  88.                 veco    dw      ?       ;vector offset
  89.                 vecs    dw      ?       ;vector segment
  90.                 unu2    dw      ?       ;unused
  91. vecblock        ends
  92.  
  93. ;***********************************************************************
  94. ;resident data structures not part of COM file
  95. changevectors   =       offset emesg            ;data area overwrites emesg & beyond
  96. vrecsize        =       8                       ;number of bytes per vector change record
  97. maxchanges      =       128                     ;maximum number of vector changes
  98. vsize           =       maxchanges*vrecsize     ;size of vector change area in bytes
  99.  
  100. ;vector table buffers
  101. origvectors     =       offset changevectors+vsize ;location of original vector table
  102. veclen          =       1024                    ;size of vector table in bytes
  103. newstackpos     =       origvectors+veclen      ;location of newstack
  104. ssize           =       128                     ;number of bytes in temporary stack
  105. newloc          =       newstackpos+ssize       ;location for relocated installation code
  106.  
  107. ;***********************************************************************
  108. ;int21 handler
  109. ;  traps functions 31, 49, 4C, and 7761
  110. int21h  proc far
  111. ifdef   debug
  112.         public  int21h
  113. endif
  114.         assume ds:nothing
  115.         pushf                           ;save flags
  116.         sti                             ;allow interrupts
  117.  
  118.         cmp     ah,31H                  ;terminate and stay resident call?
  119.         jne     @@1
  120.         call    addcurrpsp              ;dx = paras to keep
  121.         jmp     short @@4
  122.  
  123. @@1:    cmp     ah,49H                  ;deallocate block call?
  124.         jne     @@2
  125.         call    remblock                ;remove specified block if a psp
  126.         jmp     short @@4
  127.  
  128. @@2:    cmp     ah,4CH                  ;normal program halt?
  129.         jne     @@3
  130.         call    checkblocks
  131.         jmp     short @@4
  132.  
  133. @@3:    cmp     ax,7761H                ;"wa"tch ID call?
  134.         jne     @@4
  135.         call    checkblocks             ;assure change list up to date
  136.         push    bp
  137.         mov     bp,sp                   ;set up stack frame
  138.         and     word ptr [bp+8],0FFFEH  ;clear carry flag
  139.         pop     bp
  140.         xchg    ah,al                   ;flip ah and al as a signature
  141.         mov     bx,cs                   ;return WATCH psp in bx
  142.         popf
  143.         iret                            ;return to caller
  144.  
  145. @@4:    popf
  146.         jmp     dos_int                 ;let DOS take over
  147. int21h  endp
  148.  
  149. ;***********************************************************************
  150. ;int27 handler
  151. ;  watches for programs going resident
  152. int27h  proc far
  153. ifdef   debug
  154.         public int27h
  155. endif
  156.         assume ds:nothing
  157.         pushf
  158.         sti
  159.         push   dx
  160.         add    dx,15            ;pass size of block in paras to addcurrpsp
  161.         shr    dx,1
  162.         shr    dx,1
  163.         shr    dx,1
  164.         shr    dx,1
  165.         call   addcurrpsp       ;get current psp and add block to list
  166.         pop    dx
  167.         popf
  168.         jmp     tsr_int
  169. int27h  endp
  170.  
  171. ;***********************************************************************
  172. ;get current PSP in bx and add new block
  173. ;entry: dx = paragraphs to keep
  174. addcurrpsp proc near
  175. ifdef   debug
  176.         public  addcurrpsp
  177. endif
  178.         assume  ds:nothing
  179.         call    setup           ;switch stacks and save registers
  180.         assume  ds:cseg
  181.         mov     ah,51H          ;get current PSP in bx
  182.         pushf
  183.         call    dos_int
  184.         call    addblock        ;add block at bx, length dx to changes
  185.         call    shutdown        ;restore registers and switch stacks
  186.         assume  ds:nothing
  187.         ret
  188. addcurrpsp endp
  189.  
  190. ;***********************************************************************
  191. ;remove PSP block, if any, specified by es
  192. remblock proc near
  193. ifdef   debug
  194.         public  remblock
  195. endif
  196.         assume  ds:nothing
  197.         call    setup           ;switch stacks and save registers
  198.         assume  ds:cseg
  199.         mov     bx,es           ;save segment being deallocated in bx
  200.         call    matchpsp        ;return offset in changevectors of segment
  201.         or      si,si           ;any matching block?
  202.         jz      @@1
  203.         call    rempsp          ;remove psp
  204. @@1:    call    shutdown        ;restore registers and switch stacks
  205.         assume  ds:nothing
  206.         ret
  207. remblock endp
  208.  
  209. ;***********************************************************************
  210. ;scan chain of mcbs starting at segment ax
  211. ;remove halting psp from change list if needed
  212. checkchain proc near
  213. ifdef   debug
  214.         public  checkchain
  215. endif
  216. @@1:    mov     es,ax
  217.         mov     bx,es:[0001h]           ;bx = psp of block
  218.         mov     dx,es:[0003h]           ;dx = len of block
  219.         inc     ax
  220.         cmp     ax,bx                   ;does psp = mcb+1?
  221.         jne     @@2                     ;jump if not
  222.         cmp     ax,cx                   ;does psp = current program?
  223.         je      @@2                     ;jump if so
  224.         push    dx
  225.         call    matchpsp                ;find matching psp in changevectors
  226.         pop     dx
  227.         or      si,si                   ;is there a matching psp?
  228.         jnz     @@2                     ;jump if so
  229.         push    ax
  230.         push    cx
  231.         push    dx
  232.         call    addblock                ;add this psp
  233.         pop     dx
  234.         pop     cx
  235.         pop     ax
  236. @@2:    cmp     byte ptr es:[0000h],'Z' ;end of chain
  237.         je      @@3
  238.         add     ax,dx
  239.         jmp     @@1
  240. @@3:    ret
  241. checkchain endp
  242.  
  243. ;***********************************************************************
  244. ;check for new memory blocks and add if needed
  245. ;remove halting psp from change list if needed
  246. checkblocks proc near
  247. ifdef   debug
  248.         public  checkblocks
  249. endif
  250.         assume  ds:nothing
  251.         call    setup                   ;switch stacks and save registers
  252.         assume  ds:cseg
  253.  
  254.         mov     ah,51H                  ;get current psp in bx
  255.         pushf
  256.         call    dos_int
  257.  
  258.         call    matchpsp                ;is current program in change list?
  259.         or      si,si
  260.         jz      @@0                     ;jump if not
  261.         call    rempsp                  ;remove it if not
  262.  
  263. @@0:    mov     cx,bx                   ;cx = psp of halting program
  264.         mov     ax,firstmcb             ;start with first mcb
  265.         call    checkchain              ;check this chain
  266.         mov     ax,firsthimcb           ;scan high memory
  267.         or      ax,ax
  268.         jz      @@1
  269.         call    checkchain
  270. @@1:    call    shutdown                ;restore registers and switch stacks
  271.         assume  ds:nothing
  272.         ret
  273. checkblocks endp
  274.  
  275. ;***********************************************************************
  276. ;setup routine for interrupt hook routines
  277. ; switches stacks, saves registers, sets ds=cs
  278. setup   proc    near
  279. ifdef   debug
  280.         public  setup
  281. endif
  282.         assume  ds:nothing
  283.         pop     cs:tmpret       ;save return address as we switch stacks
  284.         mov     oldss,ss        ;save current stack
  285.         mov     oldsp,sp
  286.         cli                     ;switch to our stack
  287.         mov     ss,newss
  288.         mov     sp,newsp
  289.         sti
  290.         push    ax              ;store registers
  291.         push    bx
  292.         push    cx
  293.         push    dx
  294.         push    si
  295.         push    di
  296.         push    bp
  297.         push    ds
  298.         push    es
  299.         push    cs              ;set ds=cs
  300.         pop     ds
  301.         assume  ds:cseg
  302.         push    cs:tmpret       ;return
  303.         ret
  304. setup   endp
  305.  
  306. ;***********************************************************************
  307. ;shutdown routine for interrupt hook routines
  308. ; restores registers, switches stacks
  309. shutdown proc near
  310. ifdef   debug
  311.         public  shutdown
  312. endif
  313.         pop     cs:tmpret
  314.         pop     es              ;restore registers
  315.         pop     ds
  316.         assume ds:nothing
  317.         pop     bp
  318.         pop     di
  319.         pop     si
  320.         pop     dx
  321.         pop     cx
  322.         pop     bx
  323.         pop     ax
  324.         cli                     ;restore stack
  325.         mov     ss,cs:oldss
  326.         mov     sp,cs:oldsp
  327.         sti
  328.         push    cs:tmpret       ;return
  329.         ret
  330. shutdown endp
  331.  
  332. ;***********************************************************************
  333. ;add specified block to changes
  334. ;  entry: bx = psp of block, dx = length of block in paras
  335. addblock proc near
  336. ifdef   debug
  337.         public  addblock
  338. endif
  339.         assume  ds:cseg
  340.         call    addhdr          ;add a psp header block
  341.         call    addvecs         ;add blocks for each hooked vector
  342.         ret
  343. addblock endp
  344.  
  345. ;***********************************************************************
  346. ;add header for a psp block
  347. ;  entry: bx = psp of block, dx = length of block in paras
  348. ;  exit:  alters di
  349. addhdr  proc near
  350. ifdef   debug
  351.         public  addhdr
  352. endif
  353.         assume  ds:nothing
  354.         mov     di,nextchange
  355.         cmp     di,vsize-vrecsize       ;assure room for next record
  356.         ja      @@1
  357.         mov     word ptr cs:changevectors[di].id,pspid
  358.         mov     cs:changevectors[di].psp,bx
  359.         mov     cs:changevectors[di].len,dx
  360.         add     di,vrecsize
  361.         mov     nextchange,di
  362. @@1:    ret
  363. addhdr  endp
  364.  
  365. ;***********************************************************************
  366. ;add vector blocks for each hooked vector
  367. ;  entry: bx = psp of block, dx = length of block in paras
  368. ;  exit:  alters ax,cx,dx,si,di,bp
  369. addvecs proc    near
  370. ifdef   debug
  371.         public  addvecs
  372. endif
  373.         assume  ds:cseg
  374.         push    ds
  375.         add     dx,bx                   ;now dx points to end of block
  376.         mov     di,nextchange           ;cs:changevectors[di] -> output area
  377.         xor     si,si
  378.         mov     ds,si                   ;ds:si -> vectors
  379.         assume  ds:nothing
  380.         xor     cx,cx                   ;cx = vector counter
  381.         cld                             ;forward
  382.  
  383. @@1:    lodsw                           ;ax = vector offset
  384.         mov     bp,ax                   ;save vector offset
  385.         lodsw                           ;ax = vector segment
  386.  
  387.         cmp     ax,dx                   ;is vector above high limit?
  388.         jae     @@3
  389.  
  390.         push    cx
  391.         mov     cx,ax
  392.         cmp     bp,800h                 ;don't add unless a small offset
  393.         ja      @@1a                    ;(this is a trap for DOS 5 MODE)
  394.  
  395.         push    bp
  396.         shr     bp,1
  397.         shr     bp,1
  398.         shr     bp,1
  399.         shr     bp,1
  400.         add     cx,bp                   ;ax = equivalent segment of interrupt
  401.         pop     bp
  402.  
  403. @@1a:   cmp     cx,bx                   ;is vector above low limit?
  404.         jb      @@2
  405.  
  406.         pop     cx
  407.         cmp     di,vsize-vrecsize       ;room for another entry?
  408.         ja      @@3
  409.         mov     cs:changevectors[di].vec,cx ;save entry for this vector
  410.         mov     cs:changevectors[di].veco,bp
  411.         mov     cs:changevectors[di].vecs,ax
  412.         add     di,vrecsize
  413.         jmp     short @@3
  414.  
  415. @@2:    pop     cx
  416. @@3:    inc     cx                      ;next vector
  417.         cmp     cx,0FFh
  418.         jbe     @@1
  419.  
  420.         mov     nextchange,di
  421.         pop     ds
  422.         assume  ds:cseg
  423.         ret
  424. addvecs endp
  425.  
  426. ;***********************************************************************
  427. ;find changeblock matching psp
  428. ;  entry: bx = psp to match
  429. ;  exit: si = matching block, or 0 if none
  430. ;        destroys dx
  431. matchpsp proc near
  432. ifdef   debug
  433.         public  matchpsp
  434. endif
  435.         assume  ds:cseg
  436.         mov     si,offset changevectors
  437.         mov     dx,si
  438.         add     dx,nextchange           ;dx = next unused spot in changevectors
  439. @@1:    cmp     si,dx                   ;end of table
  440.         jae     @@3
  441.         cmp     word ptr [si].id,pspid  ;psp indicator?
  442.         jnz     @@2                     ;jump if not
  443.         cmp     [si].psp,bx             ;matching psp?
  444.         jnz     @@2                     ;jump if not
  445.         ret                             ;else return with match
  446. @@2:    add     si,vrecsize
  447.         jmp     @@1
  448. @@3:    xor     si,si                   ;no match if here
  449.         ret
  450. matchpsp endp
  451.  
  452. ;***********************************************************************
  453. ;remove all blocks associated with psp at offset si
  454. ;  exit: alters cx,dx,si,di,es
  455. rempsp  proc near
  456. ifdef   debug
  457.         public  rempsp
  458. endif
  459.         assume  ds:cseg
  460.         mov     di,si                   ;save destination
  461.         add     si,vrecsize             ;move to next record
  462.         mov     dx,offset changevectors
  463.         add     dx,nextchange           ;dx = address of next unused
  464. @@1:    cmp     si,dx                   ;end of table?
  465.         jae     @@2                     ;jump if so
  466.         cmp     word ptr [si].id,pspid  ;next psp indicator?
  467.         je      @@2                     ;jump if so
  468.         add     si,vrecsize             ;next block
  469.         jmp     @@1                     ;and loop
  470. @@2:    mov     cx,dx
  471.         sub     cx,si
  472.         shr     cx,1                    ;cx = words to move
  473.         push    cs
  474.         pop     es                      ;es = ds = cs
  475.         cld
  476.         rep     movsw                   ;copy down remaining blocks
  477.         sub     si,di
  478.         sub     nextchange,si           ;update nextchange
  479.         ret
  480. rempsp  endp
  481.  
  482. ;***********************************************************************
  483. ;resident portion above, temporary portion below
  484. ;***********************************************************************
  485.                 align 16
  486. emesg   db      'Cannot install WATCH more than once....',13,10,36
  487. mesg    db      'WATCH 3.5, Copyright 1993 TurboPower Software',13,10
  488.         db      'Installed successfully',13,10,36
  489. pname   db      'TSR WATCHER'
  490. plen    equ     $-pname                 ;length of string
  491.  
  492. ;***********************************************************************
  493. init    proc    near
  494. ifdef   debug
  495.         public  init
  496. endif
  497.         assume  ds:cseg
  498.  
  499. ;use int 21h test to check for previous installation
  500.         mov     ax,7761H                ;special id function
  501.         int     21H
  502.         jc      @@1                     ;not installed if function fails
  503.         cmp     ax,6177H
  504.         jnz     @@1                     ;not installed if id code not returned
  505.  
  506. ;error exit
  507.         mov    dx,offset emesg          ;error message
  508.         mov    ah,09H
  509.         int    21H                      ;DOS print string
  510.         mov    ax,4C01H                 ;exit with error
  511.         int    21H
  512.  
  513. ;not already installed
  514. @@1:    mov    dx,offset mesg           ;success message
  515.         mov    ah,09H
  516.         int    21H                      ;DOS print string
  517.  
  518. ;initialize location of WATCH stack
  519.         mov     newsp,newstackpos+ssize
  520.         mov     newss,cs                ;stack seg is code seg
  521.  
  522. ;put an id label at offset 80H to allow other programs to recognize WATCH
  523.         mov     cx,plen                 ;length of name string
  524.         mov     si,offset pname         ;offset of name string
  525.         mov     di,offset cmdline       ;offset of DOS command line
  526.         cld                             ;transfer in forward direction
  527.         mov     al,cl
  528.         stosb                           ;store length byte first
  529.         rep     movsb                   ;transfer characters
  530.  
  531. ;relocate ourselves out of the way of the resident tables
  532.         push    cs
  533.         pop     es
  534.         mov     di,newloc+10H
  535.         push    di                      ;will act as a return address
  536.         mov     si,offset @@2
  537.         mov     cx,endcode-@@2
  538.         rep     movsb                   ;move code
  539.         ret                             ;"return" to the relocated code
  540.  
  541. @@2:
  542. ;add psp records for all blocks already resident
  543.         call    adddummypsp
  544.  
  545. ;store image of original vector table (overwrites messages and non-res code)
  546.         push    cs
  547.         pop     es
  548.         mov     di,origvectors
  549.         push    ds
  550.         xor     si,si                   ;offset 0
  551.         mov     ds,si                   ;source address segment 0
  552.         mov     cx,200H                 ;512 words to store
  553.         rep     movsw                   ;copy vectors to our table
  554.         pop     ds
  555.  
  556. ;store current int 21 and 27 vectors
  557.         mov     ax,3527H
  558.         int     21H
  559.         mov     old27,bx
  560.         mov     old27[2],es
  561.         mov     ax,3521H
  562.         int     21H
  563.         mov     old21,bx
  564.         mov     old21[2],es
  565.  
  566. ;install new vectors
  567.         mov    ax,2527H
  568.         mov    dx,offset int27h
  569.         int    21H
  570.         mov    ax,2521H
  571.         mov    dx,offset int21h
  572.         int    21H
  573.  
  574. ;terminate and stay resident
  575.         mov    dx,newloc
  576.         add    dx,15
  577.         mov    cl,4
  578.         shr    dx,cl
  579.         mov    ax,3100H         ;return success code
  580.         int    21H              ;note WATCH will track itself
  581. @@3:
  582. init    endp
  583.  
  584. ;***********************************************************************
  585. ;add dummy changeblocks for all psps already resident in chain starting at ax
  586. addchain proc near
  587. ifdef   debug
  588.         public  addchain
  589. endif
  590.         assume  ds:cseg
  591. @@1:    mov     es,ax
  592.         mov     bx,es:[0001h]           ;bx = psp of block
  593.         mov     dx,es:[0003h]           ;dx = len of block
  594.         inc     ax
  595.         cmp     ax,bx                   ;does psp = mcb+1?
  596.         jne     @@2                     ;jump if not
  597.         cmp     ax,newss                ;does psp = WATCH itself?
  598.         je      @@2                     ;jump if so
  599.         mov     cx,offset addhdr
  600.         call    cx
  601. ;        call    addhdr                  ;add a header for this block
  602. @@2:    cmp     byte ptr es:[0000h],'Z' ;end of chain
  603.         je      @@3
  604.         add     ax,dx
  605.         jmp     @@1
  606. @@3:    ret
  607. addchain endp
  608.  
  609. ;***********************************************************************
  610. findhidos proc near
  611. ;returns ax = first high mcb, or 0 if none found
  612.         mov     ah,52H                  ;get first mcb segment
  613.         int     21H
  614.         mov     ax,es:[bx-2]            ;ax=first mcb
  615.  
  616.         push    ds
  617.         assume  ds:nothing
  618.  
  619. @@1:    cmp     ax,9FFFH                ;above 640K?
  620.         ja      @@2                     ;jump if so
  621.         mov     ds,ax                   ;ds:[0] points to mcb
  622.         xor     ax,ax                   ;set ax to zero in case we exit here
  623.         cmp     byte ptr ds:[0],'Z'     ;end of mcb chain?
  624.         je      @@2                     ;exit if so
  625.         mov     ax,ds                   ;restore ax to mcb segment
  626.         inc     ax                      ;skip over mcb itself
  627.         add     ax,ds:[3]               ;add length of memory block
  628.         jmp     @@1
  629.  
  630. @@2:    pop     ds                      ;ds = cs
  631.         assume  ds:cseg
  632.         ret
  633. findhidos endp
  634.  
  635. ;***********************************************************************
  636. ;return segment of first high memory mcb in ax
  637. findhimemstart proc near
  638. ifdef   debug
  639.         public findhimemstart
  640. endif
  641.  
  642.         mov     ax,5802h
  643.         int     21H                     ;get umb link status
  644.         or      al,al                   ;high memory already linked?
  645.         jz      @@u1                    ;jump if not
  646.  
  647.         call    findhidos               ;find mcb using linked chain
  648.  
  649.         or      ax,ax                   ;valid mcb seg returned?
  650.         jz      @@u9                    ;jump if not
  651.         mov     cx,ax
  652.         jmp     @@9                     ;found first umb
  653.  
  654. @@u1:   mov     ax,5803H
  655.         mov     bx,1
  656.         int     21H                     ;link umb
  657.         jc      @@u9                    ;jump if failed
  658.  
  659.         call    findhidos               ;find mcb using linked chain
  660.  
  661.         push    ax                      ;save result
  662.         mov     ax,5803H
  663.         xor     bx,bx
  664.         int     21H                     ;unlink umb
  665.         pop     ax                      ;restore result
  666.  
  667.         or      ax,ax                   ;valid mcb seg returned?
  668.         jz      @@u9                    ;jump if not
  669.         mov     cx,ax
  670.         jmp     @@9                     ;found first umb
  671.  
  672. @@u9:   mov     ax,3000h                ;get DOS version
  673.         int     21H
  674.         cmp     al,3
  675.         jb      @@7                     ;no XMS driver possible
  676.         mov     ax,4300h
  677.         int     2Fh                     ;multiplex call for XMS
  678.         cmp     al,80h                  ;proper signature?
  679.         jne     @@7                     ;no XMS driver
  680.         mov     ax,4310h
  681.         int     2Fh
  682.         mov     xmsxxx,bx               ;save XMS control address
  683.         mov     xmsxxx[2],es
  684.         mov     ah,10h
  685.         mov     dx,0FFFFh
  686.         call    xmsadr                  ;ask to allocate FFFF paras of UMB
  687.         cmp     bl,0B0h                 ;will fail with B0 if UMBs avail
  688.         je      @@0
  689.         cmp     bl,0B1h                 ;will fail with B1 if UMBs all allocated
  690.         jne     @@7                     ;no UMBs exist
  691. @@0:    int     12H
  692.         mov     cl,6
  693.         shl     ax,cl                   ;get segment of top of memory
  694.  
  695. @@1:    mov     es,ax
  696.         cmp     byte ptr es:[0000h],'M' ;potential mcb?
  697.         jnz     @@6                     ;not an mcb, try next segment
  698. @@2:    mov     cx,ax                   ;save potential start mcb in cx
  699. @@3:    inc     ax
  700.         add     ax,es:[0003h]           ;ax = start of next mcb
  701.         jc      @@5                     ;can't be an mcb if we wrapped
  702.         mov     es,ax                   ;address of next mcb
  703.         mov     dl,es:[0000h]
  704.         cmp     dl,'M'
  705.         jz      @@3                     ;good start mcb
  706.         cmp     dl,'Z'
  707.         jz      @@9                     ;good end mcb
  708. @@5:    mov     ax,cx                   ;restore last start segment
  709. @@6:    cmp     ax,0FFFFh               ;top of memory?
  710.         je      @@7
  711.         inc     ax                      ;try next segment
  712.         jmp     @@1
  713.  
  714. @@7:    xor     cx,cx                   ;no matching UMB
  715. @@9:    mov     ax,cx                   ;return segment in ax
  716.         ret
  717. findhimemstart endp
  718.  
  719. ;***********************************************************************
  720. ;add dummy changeblocks for all psps already resident
  721. adddummypsp proc near
  722. ifdef   debug
  723.         public  adddummypsp
  724. endif
  725.         assume  ds:cseg
  726.         mov     ah,52H
  727.         int     21H                     ;get DOS list of lists
  728.         mov     ax,es:[bx-2]            ;get first MCB segment
  729.         mov     firstmcb,ax             ;save it for use later too
  730.         call    addchain
  731.  
  732.         call    findhimemstart          ;find first high memory mcb
  733.         mov     firsthimcb,ax           ;save it for use later too
  734.         or      ax,ax
  735.         jz      @@1
  736.         call    addchain                ;add blocks in high memory too
  737. @@1:    ret
  738. adddummypsp endp
  739.  
  740. endcode:
  741. cseg    ends
  742.         end     pentry
  743.